'''
    This file is part of Ognom Keeper.

    Ognom Keeper is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Ognom Keeper is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Ognom Keeper.  If not, see <http://www.gnu.org/licenses/>.


    Copyright(C) 2009 Viktor Vestberg
'''

try: 
    from utils import *
except ImportError, err:
    print str("couldn't load module. %s" % (err))

class Entity:
    """The Entity class is a container of parts, what defines what a entity can do is its parts
        otherwise its just an empty shell with diffrent data. The entity updates all its parts every tick"""
    def __init__(self, position):
        self.parts = []    # List of all its parts, this is incremented in addparts()
        self.handler = None    # Entities keep track of their EntityHandler, mostly to be able to destroy itself properly
        self.verts = [(-5,-5),(-5,0),(0,-5),(0,0)] # This is just used by walls and stuff like that
        self.X = position[0] 
        self.Y = position[1]        # The positions of the entity. This attribute is often controlled by an physicspart as they calculate the position        
        self.angle = 0
        self.boost = 3                # Boost multiplies the current force of the physicspart when a button is pressed
        self.force = 1700            # Force is the amout of force the physicspart will gain when trying to move
        self.torque = 4000        # The speed of the physicsparts rotation
        self.health = 2000
        self.impulseForce = 10
    def addParts(self, parts):    # Adds a list of parts
        for part in parts:
            self.addPart(part)
    def addPart(self, part):    #Same thing as Addparts but you add one part
        self.parts.append(part)
        part.setParent(self)
        part.init()
    def getHealth(self):
        return self.health
    def update(self):
        for part in self.parts: part.update()
    def remove(self):    
        for part in self.parts: part.remove()
        self.parts = []
        try: del self.handler.entityList[self.handler.entityList.index(self)] 
        except ValueError: pass
    def setHandler(self, handler): 
        self.handler = handler
    def setPos(self, pos):
        self.X = pos[0]
        self.Y = pos[1]
    def getPos(self):
        return (self.X, self.Y)
class Part:
    """This is an abstract class which all parts inherit from it simply has different
         values and methods to prevent errors"""
    def __init__(self):
        self.parent = None 
        self.handler = None   
    def setParent(self, parent):    
        self.parent = parent
    def setHandler(self, handler):    
        self.handler = handler
    def init(self):    
        pass
    def update(self):    
        pass
    def remove(self):
        pass
    
class PhysicsPart(Part):
    """The abstract class for all physicparts, this allow them to be steered"""
    def __init__(self):
        self.shape.elasticity = 0.5
        self.shape.friction = 0.5
    def turnLeft(self):    
        self.body.torque -= self.parent.torque
    def turnRight(self):
        self.body.torque += self.parent.torque
    def forceForward(self):
        self.body._bodycontents.f[0] -= math.cos(self.body.angle+(math.pi/2))*self.parent.force
        self.body._bodycontents.f[1] -= math.sin(self.body.angle+(math.pi/2))*self.parent.force
    def forceBackward(self):
        self.body._bodycontents.f[0] += math.cos(self.body.angle+(math.pi/2))*self.parent.force
        self.body._bodycontents.f[1] += math.sin(self.body.angle+(math.pi/2))*self.parent.force
    def forceBoost(self):
        self.body._bodycontents.f[0] *= self.parent.boost
        self.body._bodycontents.f[1] *= self.parent.boost
    def jump(self):
        self.body.apply_impulse(Vec2d(self.parent.impulseForce*math.cos(to_radian(self.parent.angle)), self.parent.impulseForce*math.sin(to_radian(self.parent.angle))), Vec2d(0.0, 0.0))
    
class Renderable(Part, pygame.sprite.Sprite):
    """The RenderablePart class. This takes a ready picture as parameter which will be provided by its Handler"""
    def __init__(self, visible):
        pygame.sprite.Sprite.__init__(self) 
        self.image, self.rect = visible
        self.original_image = self.image    #We keep track of how the image original state for image rotation
    def update(self):
        self.rect.center = self.parent.X, self.parent.Y # This updates the renderables position according to its parent
        self.image = pygame.transform.rotate(self.original_image, -self.parent.angle) # This rotates the renderable according to its parent angle
    def remove(self):    
        self.kill() 
        del self.handler.parts[self.handler.parts.index(self)]
        
class RectPart(Part, pygame.sprite.Sprite):
    """The RenderableRectPart draws a rectangle according to the paremeter color and size """
    def __init__(self, color, size):
        pygame.sprite.Sprite.__init__(self) 
        self.image = pygame.Surface(size)   
        self.rect = self.image.get_rect()      
        self.image.set_colorkey([255,0,255], pygame.RLEACCEL)   
        self.image.fill([255,0,255])    # This fills the surface with the colorkey color, to make everything thats not drawed upon transparent
        pygame.draw.rect(self.image, color, self.rect)
        self.original_image = self.image 
    def update(self):
        self.rect.center = self.parent.X, self.parent.Y # This updates the renderables position according to its parent
        self.image = pygame.transform.rotate(self.original_image, -self.parent.angle)  #This rotates the renderable according to its parent angle
    def remove(self):
        self.kill() 
        del self.handler.Renderparts[self.handler.parts.index(self)]
        
class LinePart(Part, pygame.sprite.Sprite):
    """This draws a series of lines. This renderable is special as it is not rendered upon an 
        induvidual surface, but a backgroundsurface that is provided by the Handler"""
    def __init__(self, color, surface=None ):
        pygame.sprite.Sprite.__init__(self)
        if surface is not None: self.image = surface
        else: self.image = pygame.display.get_surface()
        self.rect = self.image.get_rect()
        self.color = color
        self.updated = False
    def init(self):
        # Lineparts dont actually update, you draw them once on a surface, then you have to do cleanBlit to remove them
        pygame.draw.aalines(self.image, self.color,  False, self.parent.verts)
        self.handler.blit()
    def remove(self):
        del self.handler.shapes[self.handler.shapes.index(self)]
        self.kill()
        self.handler.blit()

class CirclePart(Part, pygame.sprite.Sprite):
    """The RenderableCirclePart draws a circle according to the parameters radian size and color
        the size is the thickness of the circle line"""
    def __init__(self, radian, size, color):
        pygame.sprite.Sprite.__init__(self)  
        self.image = pygame.Surface((radian*2,radian*2)) # Creates a surface with the size so that the circle can fit inside
        self.rect = self.image.get_rect() 
        self.image.set_colorkey([255,0,255], pygame.RLEACCEL) 
        self.image.fill([255,0,255])
        pygame.draw.circle(self.image, color, (radian, radian), radian, size)
        pygame.draw.aaline(self.image,[255,255,255],(0,self.rect.height/2), (self.rect.right,self.rect.height/2))
        self.original_image = self.image
    def init(self):
        self.rect.center = self.parent.X, self.parent.Y
    def update(self):
        self.rect.center = self.parent.X, self.parent.Y 
        self.image = pygame.transform.rotate(self.original_image, -self.parent.angle)
    def remove(self):
        self.kill()
        del self.handler.parts[self.handler.parts.index(self)]

class BallPart(PhysicsPart):
    def __init__(self, space, radius, mass):
        self.space = space # The chipmunk space is required
        self.body = Body(mass, 6000)    # Creates a new physicsbody with the parameters (mass, moment of inertia)
        self.shape = Circle(self.body, radius, Vec2d(0,0)) # Creates a new circleshape 
        space.add(self.body, self.shape) #Add the body and shape to the space
        self.shape.collision_type = 1    # If you want to know what collision types are for, read up on collision functions
        self.shape.friction = 0.7
        self.shape.elasticity = 0.5
    def init(self):
        self.body.position = Vec2d(self.parent.X, self.parent.Y)
    def update(self):
        self.body.reset_forces() 
        self.parent.X, self.parent.Y = self.body.position
        self.parent.angle = to_degree(self.body.angle)    
    def remove(self):
        self.space.remove(self.shape) 
        self.space.remove(self.body)
        del self.handler.parts[self.handler.parts.index(self)]

class WallPart(Part):
    def __init__(self, space, verticies, mass, colType, elasticity):
        self.shapes = [] # This is a list of shapes as the wall can consist of a series of walls
        self.space = space
        self.verticies = verticies
        self.body = Body(mass, 1e100) # Make the physicsPart body
        for i in range( len( verticies ) -1): # Makes a shape for every two verticies, combined they are a wall
            shape = Segment(self.body, Vec2d(verticies[i]), Vec2d(verticies[i+1]), 0.0) # This creates the segmentshape
            self.space.add_static(shape)
            self.shapes.append(shape)    # To keep track of all the shapes
            shape.friction = 0.7
            shape.elasticity = elasticity
            shape.collision_type = colType
    def init(self):
        self.parent.verts = self.verticies # Temporarly hack to give for example an RenderableLinePart the verticies it requires
    # Removes the physicsPart and all its shapes
    def remove(self):
        for shape in self.shapes: self.space.remove_static(shape)
        self.shapes = []
        del self.handler.staticParts[self.handler.staticParts.index(self)]

class InputPart(Part):
    """The InputPart is a sort of container for Keyparts, it checks if its containing keys are pressed"""
    def __init__(self):
        self.parts = [] # The list of keyparts
    # Adds parts in a list
    def addParts(self, parts):    
        for part in parts:
            self.parts.append(part)
    def update(self):
        for part in self.parts:
            if pygame.key.get_pressed()[part.key]: part.action() # Action() is the keyparts containing function that will be run when its key is pressed
    # Remove and cleanup
    def remove(self): 
        for part in self.parts: part.remove()
        self.parts = []
        del self.handler.parts[self.handler.parts.index(self)]
                
class TextPart(Part, pygame.sprite.Sprite):
    """See the TextPart class. This is the same but we simply send a string to it that it will render"""
    def __init__(self, data, position, size, color, antialiasing, font):
        pygame.sprite.Sprite.__init__(self)
        self.sprite = pygame.sprite.Group()
        self.updateAble = True
        self.color = color
        self.data = data
        self.antialiasing = antialiasing
        self.font = pygame.font.Font(os.path.join('data', font), size)
        self.image = self.font.render(str(self.data()), False, self.color)
        self.rect = self.image.get_rect()
        self.rect.top, self.rect.left = position
        self.sprite.add(self)
    def update(self):
        self.image = self.font.render(str(self.data()), self.antialiasing, self.color)
    def remove(self):
        self.kill()
        del self.handler.parts[self.handler.parts.index(self)]
        
class StaticText(Part, pygame.sprite.Sprite):
    """See the TextPart class. This is the same but we simply send a string to it that it will render"""
    def __init__(self, data, position, size, color, antialiasing, font):
        pygame.sprite.Sprite.__init__(self)
        self.sprite = pygame.sprite.Group()
        self.updateAble = True
        self.color = color
        self.data = data    # What string should be displayed
        self.antialiasing = antialiasing
        self.font = pygame.font.Font(os.path.join('data', font), size)
        self.image = self.font.render(self.data, False, self.color)
        self.rect = self.image.get_rect()
        self.rect.top, self.rect.left = position
        self.sprite.add(self)
    def update(self):
        self.image = self.font.render(str(self.data), self.antialiasing, self.color)
    def remove(self):
        self.kill()
        del self.handler.parts[self.handler.parts.index(self)]     
        
class KeyPart(Part):
    """The keyparts contain a key (pygame.K) and a method. 
        If the parts key is pressed, the method will be called. You can also send a parameter to the method"""
    def __init__(self, key, actions, parameter):
        self.key = key     # The key attribute
        self.actions = []    # List of actions, in case we want to have multiple methods to run when the key is pressed
        self.parameter = parameter # The parameter, this is not required
        for action in actions: self.actions.append(action)
    # This will be problably called by the InputHandler when it detects that the parts key is pressed
    def action(self): 
        for action in self.actions: 
            if self.parameter is None: action()
            else: action(self.parameter)
    def addAction(self, action): 
        self.actions.append(action)
    def removeAction(self, action): 
        del self.actions[self.actions.index(action)]
    def remove(self):
        self.actions = []
        del self.handler.keyparts[self.handler.keyparts.index(self)]

class MouseKeyPart(Part):
    """This is the same as the KeyPart but it detects input from the mouse. The mousebuttons is stored as integers: 1, 2, 3"""
    def __init__(self, key, actions, parameter):
        self.key = key
        self.actions = []
        self.parameter = parameter
        for action in actions: self.actions.append(action)
    def action(self):
        for action in self.actions:
            if self.parameter is None: action()
            else: action(self.parameter)
    # Incase we want to add moar actions
    def addAction(self, action): 
        self.actions.append(action)
    # Incase we need less actions
    def removeAction(self, action): 
        del self.actions[self.actions.index(action)]
    def setKey(self, key):
        self.key = key

class ObserverEvent:
    """You can send a observerevent to a InputPart if you want multiple things to happen when a keypress
    happen"""
    def __init__(self):
        self.Events = []
    def activate(self):
        for event in self.Events: event()
    def addEvents(self, events):
        for event in events: self.Events.append(event)
    def addEvent(self, event):
        self.Events.append(event)
        
class MenuPart(Part):
    """The MenuPart is a complete Part to make a menuitem. 
        It shows text then when clicked upon, the stored action will be run"""
    def __init__(self, Method, Parameter, ActiveColor, InActiveColor):
        self.mouseKeyPart = None # This is for clicking on the menuitem
        self.method = Method # This will be run when the menuitem is clicked upon
        self.active = False    # The MenuPart will be active when the mouse is hovering over it
        self.activeColor = ActiveColor
        self.inActiveColor = InActiveColor
        self.parameter = Parameter 
    # This method runs when we click on the menupart
    def activate(self): 
        if self.active: 
            if self.parameter is None: self.method()
            else: self.method(self.parameter)
    def update(self):
        if self.active: self.textPart.color = self.activeColor
        else: self.textPart.color = self.inActiveColor
        self.textPart.update()
    def setHover(self, bool): 
        self.active = bool
    def setTextpart(self, Part):
        self.textPart = Part
        self.textPart.updateAble = False
    def setMouseKeypart(self, Part):
        self.mouseKeyPart = Part
        
class EnterTextPart(Part):
    """This is a sort of widget created by the EnterText handler, its a simple thingy for entering a string"""
    def __init__(self):
        self.string = 'unnamed'
        self.limit = 15
        self.text = None
    def update(self):
        self.text.data = self.string
        self.text.update()
    def setTextPart(self, part):
        self.text = part
    def removeLetter(self):
        try :
            lastletter = self.string[-1]
            self.string = self.string.rstrip(lastletter)
        except IndexError: pass
    def addLetter(self, string):
        if len(self.string) < self.limit: self.string = self.string+string
    def remove(self):
        self.text.remove()
        del self.handler.parts[self.handler.parts.index(self)]
    def __str__(self):
        return self.string
        
